package groovy.jmx.builder.modelmbean

import groovy.jmx.builder.node.MBeanNodeData
import groovy.jmx.builder.node.OpNodeData
import javax.management.Descriptor
import javax.management.MBeanOperationInfo
import javax.management.MBeanParameterInfo
import javax.management.modelmbean.DescriptorSupport
import javax.management.modelmbean.ModelMBeanOperationInfo




class ModelMBeanOperationCollector {
    def static exemptMethods = ['getProperty', 'setProperty', 'getMetaClass', 'setMetaClass', 'invokeMethod']

    public List<ModelMBeanOperationInfo> collectOperationInfo(MBeanNodeData data) {
        List<ModelMBeanOperationInfo> result = []
        List<MetaMethod> methods = data?.object.metaClass.methods

        if (methods) {
            methods.each {method ->
                if (
                        method.declaringClass.name.equals(data.object.class.name)
                                && !method.name.startsWith("get")
                                && !method.name.startsWith("set")
                                && !exemptMethods.contains(method.name)
                ) {
                    ModelMBeanOperationInfo info
                    OpNodeData opNode = CollectorHelper.getOpNodeData(data, method)
                    if (opNode) {
                        info = getMBeanOperationInfo(method, opNode)
                        result.add(info)
                    }
                }
            }
        }
        return result
    }

    public List<ModelMBeanOperationInfo> collectOperationInfo(def instance) {
        List<ModelMBeanOperationInfo> result = []
        List<MetaMethod> methods = instance?.metaClass.methods

        if (methods) {
            methods.each {method ->
                if (
                        method.declaringClass.name.equals(instance.class.name)
                                && !method.name.startsWith("get")
                                && !method.name.startsWith("set")
                                && !exemptMethods.contains(method.name)
                ) {
                    ModelMBeanOperationInfo info
                    info = getMBeanOperationInfo(method)
                    result.add(info)
                }
            }
        }
        return result
    }

    private ModelMBeanOperationInfo getMBeanOperationInfo(MetaMethod op, OpNodeData data) {
        ModelMBeanOperationInfo opInfo;
        if (data && op) {
            Descriptor desc = new DescriptorSupport()
            desc.setField("name", op.name)
            desc.setField("descriptorType", "operation")
            desc.setField("role", "operation")
            desc.setField("displayName", data.description)
            desc.setField("class", op.declaringClass.name)

            // add any addtional fields
            data?.value.each {key, value ->
                desc.setField(key, value)
            }

            MBeanParameterInfo[] params = CollectorHelper.collectParamInfo(data)?.toArray()
            opInfo = new ModelMBeanOperationInfo(
                    op.name,
                    data.description,
                    params,
                    op.returnType.name,
                    MBeanOperationInfo.ACTION,
                    desc
            );

        }
        return opInfo
    }

    private ModelMBeanOperationInfo getMBeanOperationInfo(MetaMethod op) {
        ModelMBeanOperationInfo opInfo;
        if (op) {
            Descriptor desc = new DescriptorSupport()
            desc.setField("name", op.name)
            desc.setField("descriptorType", "operation")
            desc.setField("role", "operation")
            desc.setField("displayName", "Method")
            desc.setField("class", op.declaringClass.name)

            MBeanParameterInfo[] params = CollectorHelper.collectParamInfo(op)?.toArray()
            opInfo = new ModelMBeanOperationInfo(
                    op.name,
                    "Method",
                    params,
                    op.returnType.name,
                    MBeanOperationInfo.ACTION,
                    desc
            );

        }
        return opInfo
    }

}